// Copyright (c) 2013, Jose L. Hidalgo Valiño <pplux@pplux.com>
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#ifndef __SLB3_CLASS__
#define __SLB3_CLASS__ 1

#include "../namespace.h"

namespace SLB3 {
namespace internal {

  template<class T>
  class Class : public Namespace {
  public:
    Class& constructor();
    template<class A1>
    Class& constructor();
    template<class A1, class A2>
    Class& constructor();
    template<class A1, class A2, class A3>
    Class& constructor();
    template<class A1, class A2, class A3, class A4>
    Class& constructor();
    template<class A1, class A2, class A3, class A4, class A5>
    Class& constructor();
    template<class P>
    Class& inherits();

    template<class V>
    Class& set(const char *name, V value) {
      Namespace::set<V>(name, value);
      return *this;
    }

    template<class V>
    Class& setMetamethod(const char *name, V v);

  private:
    Class(lua_State *L, int metatable_index);
    ~Class();
    const int meta_;
    void set_call_metamethod(lua_CFunction f);
#if defined(_MSC_VER)
  // Invalid warning generated by Visual Studio
  #pragma warning(disable: 4396)
#endif
    friend void PushMetatable<T>(lua_State*);
#if defined(_MSC_VER)
  #pragma warning(default: 4396)
#endif
  };


  template<class T>
  inline void PushMetatable(lua_State *L) {
    if (luaL_newmetatable(L, Wrapper<T>::GetName())) {
      Class<T> class_helper(L,lua_gettop(L));
      Wrapper<T>::Register(class_helper);
    }
  }

  void PushEnumMetatable(lua_State *L, const char *enum_name);

} // internal namespace

  template<class T>
  inline void Register(lua_State *L) {
    // Trying to get the metatable will force to generate the bindings
    // or do nothing. Either way we pop the result and leave the stack as
    // it was
    internal::PushMetatable<T>(L);
    lua_pop(L,1);
   }


}  // SLB3 namespace

#endif


#include "class_imp.h"
